"""
权限控制系统模块

该模块提供了基于角色和声明的权限控制功能，
包括权限基类、权限组合器以及具体的权限实现。
"""

from fastapi import HTTPException, Request
from starlette import status

from app.auth.models import Api, GenericPermissions
from app.core.handlers.auth import decode_token, extract_credentials


class Permission:
    """
    权限基类

    所有权限类都应该继承此类，并实现has_permission方法。
    """

    # 默认状态码
    status_code = status.HTTP_401_UNAUTHORIZED
    # 默认错误详情
    detail = "权限不足"

    async def __call__(self, request: Request):
        if await self.has_permission(request):
            return
        await self.raise_exception()

    async def has_permission(self, request: Request) -> bool:
        """
        检查是否有权限

        Args:
            request (Optional[Request]): HTTP请求对象

        Returns:
            bool: 有权限返回True，否则返回False

        Raises:
            NotImplementedError: 子类必须实现此方法
        """
        raise NotImplementedError

    # 抛出异常
    async def raise_exception(self):
        """
        抛出权限不足异常
        """
        raise HTTPException(self.status_code, self.detail)


class Operator:
    def __init__(self, *permissions: type[Permission] | Permission):
        self.permissions: list[Permission] = []
        for permission in permissions:
            if isinstance(permission, type) and issubclass(permission, Permission):
                # 如果是Permission类，则实例化
                self.permissions.append(permission())
            elif isinstance(permission, Permission):
                # 如果已经是Permission实例，则直接添加
                self.permissions.append(permission)
            else:
                # 其他情况抛出异常或处理
                raise TypeError(f"期望 Permission 类或实例，得到 {type(permission)}")


class And(Permission, Operator):
    async def has_permission(self, request: Request):
        for permission in self.permissions:
            if not (await permission.has_permission(request)):
                self.status_code = permission.status_code
                self.detail = permission.detail
                return False

        return True


class Or(Permission, Operator):
    async def has_permission(self, request: Request):
        for permission in self.permissions:
            if await permission.has_permission(request):
                return True
            self.status_code = permission.status_code
            self.detail = permission.detail

        return False


# 权限
class IsAdmin(Permission):
    """
    管理员权限类

    检查当前用户是否为管理员。
    """

    detail = "需要管理员权限"

    async def has_permission(self, request: Request) -> bool:
        """
        检查用户是否为管理员

        Args:
            request (Optional[Request]): HTTP请求对象

        Returns:
            bool: 用户是管理员返回True，否则返回False
        """
        user = request.user
        if not user:
            return False

        return user.is_admin


class APIWhitelist(Permission):
    """
    API白名单权限类

    检查请求路径是否在白名单中。
    """

    detail = "用户不在白名单"
    # 白名单路径列表
    white_list = ["/api/v1/auth/login", "/openapi.json", "/docs", "/test"]

    async def has_permission(self, request: Request) -> bool:
        """
        检查请求路径是否在白名单中

        Args:
            request (Optional[Request]): HTTP请求对象

        Returns:
            bool: 路径在白名单中返回True，否则返回False
        """
        path = request.url.path
        if path in self.white_list:
            return True
        return False


class IsAuthorized(Permission):
    """
    授权检查权限类

    检查用户是否已通过身份验证。
    """

    detail = "用户未授权"

    async def has_permission(self, request: Request) -> bool:
        """
        检查用户是否已通过身份验证

        Args:
            request (Optional[Request]): HTTP请求对象

        Returns:
            bool: 用户已授权返回True，否则返回False
        """
        credentials = await extract_credentials(request)
        if not credentials:
            return False

        if not decode_token(credentials.credentials):
            return False

        return True


class IsApiAccess(IsAuthorized):
    """
    API访问权限类

    检查用户是否允许访问API。
    """

    async def has_permission(self, request: Request) -> bool:
        if not await super().has_permission(request):
            return False

        user_permissions: list[GenericPermissions] = request.user.permissions
        api: Api = request.state.api
        for permission in user_permissions:
            if permission.code == f"api_{api.code}_!access":
                return False

        return True
